package pl.edu.mimuw.irs.core.model.analysis.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import pl.edu.mimuw.irs.core.model.analysis.Time;
import pl.edu.mimuw.irs.core.model.analysis.dd.ActivityWrapper;
import pl.edu.mimuw.irs.core.model.analysis.resources.Fire;
import pl.edu.mimuw.irs.core.model.analysis.resources.Hire;
import pl.edu.mimuw.irs.core.model.analysis.resources.RequirementPlace;
import pl.edu.mimuw.irs.core.model.analysis.resources.ResourcePlace;
import pl.edu.mimuw.irs.core.model.design.resource.Role;
import pl.edu.mimuw.irs.core.model.petrinet.Transition;
import pl.edu.mimuw.irs.core.util.Pair;

public class EventsUtils {

	// *** Attributes

	// *** Methods
	
	public static synchronized List<Pair<String, String>> asPairsList(Map<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>> events) {
		List<Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>>> sorted = 
			sort(events);
		
		List<Pair<String, String>> pairsArray = new ArrayList<Pair<String, String>>(sorted.size());
		
		for (Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>> concEvents : sorted) {
			Pair<String, String> pair = formatConcEvents(concEvents);
			pairsArray.add(pair);
		}
		
		return pairsArray;
	}

	private static synchronized Pair<String, String> formatConcEvents(
			Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>> concEvents) {
		String key = formatTime(concEvents.getKey());
		String value = formatEvents(concEvents.getValue());
		
		return new Pair<String, String>(key, value);
	}

	private static String formatTime(Time time) {
		return String.valueOf(time.getValue());
	}

	private static synchronized String formatEvents(
			Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>> events) {
		StringBuilder builder = new StringBuilder();
		boolean first = true;
		for (Entry<Transition, Map<ResourcePlace, Set<RequirementPlace>>> event : events.entrySet()) {
			if (event.getKey() instanceof Hire) {
				Hire hire = (Hire) event.getKey();
				if (first) {
					first = false;
				} else {
					builder.append(", ");
				}
				builder.append(formatEvent(hire, event.getValue()));
			} else if (event.getKey() instanceof Fire) {
				Fire fire = (Fire) event.getKey();
				if (first) {
					first = false;
				} else {
					builder.append(", ");
				}
				builder.append(formatEvent(fire, event.getValue()));
			} else continue; /* Omit non-hire and non-fire transitions */
		}
		return builder.toString();
	}

	private static String formatEvent(Hire hire,
			Map<ResourcePlace, Set<RequirementPlace>> value) {
		return "H" + formatPair(hire.getActivity(), value);
	}

	private static String formatEvent(Fire fire,
			Map<ResourcePlace, Set<RequirementPlace>> value) {
		return "F" + formatPair(fire.getActivity(), value);
	}

	private static String formatPair(ActivityWrapper activity,
			Map<ResourcePlace, Set<RequirementPlace>> assignments) {
		StringBuilder builder = new StringBuilder();
		builder.append("{");
		boolean first = true;
		for (Entry<ResourcePlace, Set<RequirementPlace>> entry : assignments.entrySet()) {
			for (RequirementPlace requirement : entry.getValue()) {
				if (first) {
					first = false;
				} else {
					builder.append(",");
				}
				builder.append("<");
				builder.append("{");
				boolean first2 = true;
				for (Role role : requirement.getRequirement().getRolesMap().keySet()) {
					if (first2) {
						first2 = false;
					} else {
						builder.append(",");
					}
					builder.append(role);
				}
				builder.append("}, ");
				builder.append(entry.getKey().getResource().getName());
				builder.append(">");
			}
		}
		builder.append("}");
		return "(" + activity.getActivity().getRequirement().getName() + ", " + builder.toString() + ")";
	}

	private static synchronized List<Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>>> sort(
			Map<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>> events) {
		List<Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>>> arrayList = 
			new ArrayList<Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>>>();
		
		for (Entry<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>> entry : events.entrySet()) {
			Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>> pair =
				new Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>>(entry.getKey(), entry.getValue());
			arrayList.add(pair);
		}
		
		Collections.sort(arrayList, new EventsComparator());
		
		return arrayList;
	}
	
	private static class EventsComparator implements 
		Comparator<Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>>> {

		@Override
		public int compare(
				Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>> o1,
				Pair<Time, Map<Transition, Map<ResourcePlace, Set<RequirementPlace>>>> o2) {
			return o1.getKey().compareTo(o2.getKey());
		}
		
	}
}
